/*
 * resmanager.cpp
 * This file is part of OpenAnimator
 *
 * Copyright (C) 2008 - Abhinav Lele
 *
 * OpenAnimator is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * OpenAnimator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenAnimator; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */
#include "resmanager.h"

ResManager::ResManager() {
    T("ResManager()")
    isInit = 0;
    cout << time(NULL) << endl;
    stringstream sconv;
    #if defined(WIN32) || defined(_WIN32)
		sconv << std::getenv("TEMP");
		sconv << "/" << time(NULL) << "/";
		resroot = sconv.str();
		isInit = !mkdir(sconv.str().c_str());
		for(int i=0;i<resroot.size();++i) { if(resroot[i] == '/') resroot[i] = '\\'; }
		M("ResManager: ResourceRoot=" << sconv.str() << " Result: " << (isInit?"SUCCESS":"FAILED") )
	#else
		char tempdirname[1024];
		char *tmptemplate = P_tmpdir;
		if(strlen(tmptemplate) > (1015)) {
			E("ResManager: Temporary file template too long")
			isInit = 0;			return;
		}
		strcpy(tempdirname,tmptemplate);
		strcat(tempdirname,"/oaXXXXXX");
		M("ResManager: Trying to create :" << tempdirname)
		char *res = mkdtemp(tempdirname);
		M("ResManager: Got "<< res)
		if(res == NULL) { E("ResManager: Unable to create temporary directory") isInit = 0; return; }
		resroot = string(res);
		isInit = 1;
    #endif

#if defined (WIN32) || defined (_WIN32)
//    isInit = !CreateDirWithSecurity(sconv.str().c_str());
#else
//    isInit = !mkdir(sconv.str().c_str(),0700);
#endif

   /*
	char *apath = std::getenv("SZIPPATH");
    if(apath == NULL) {
        archivemanagerpath = "";
        E("ResManager: SZIPPATH not set. For ResManager to work set the 7ZIPPATH to the location of 7z.exe or equivalent executable in your environment")
//	#if defined(WIN32) || defined (_WIN32)
 //       RemoveDirectory(resroot.c_str());
//	#else
//	#endif
		rmdir(resroot.c_str());
        isInit = 0;
    }
    else {    archivemanagerpath = apath; M("ResManager: 7ZIPPATH=" << apath ) }
   
    */
    
}

int ResManager::registerid(int resid,string respath) { /**< respath is relative path */
			T("ResManager:registerid: " << resid << "," << respath)
        	for(int i=0;i<respath.size();i++) 
        		if(respath[i] == '.') { E("ResManager:registerid: Illegal character .") return 0; }
        	
        	ifstream inres((resroot + 
        	#if defined(WIN32) || defined(_WIN32)
        	string("\\")
        	#else
        	string("/")
        	#endif
        	+ respath).c_str());
        	if(!inres.good())  {
        		E("ResManager:registerid: Unable to verify file read access.")
        		return 0;
        	}
	        
	        if(resloaded.find(resid) != resloaded.end()) {
	        	E("ResManager:registerid: An item is already reigstered")
	        	return 0;
	        }
	        resloaded[resid] = resroot + 
        	#if defined(WIN32) || defined(_WIN32)
        	string("\\")
        	#else
        	string("/")
        	#endif
        	+ respath;
	        return 1;
        }

ResManager::~ResManager() {
    T("~ResManager")
//    #if defined(WIN32) || defined(_WIN32)
        if(isInit) {
            int res = rmdir_recursive(resroot);
            if(res) 
                M("~ResManager: Removing ResRoot result: SUCCESS" )
            else
                E("~ResManager: Removing ResRoot result: FAILED" )
        }
//    #endif
}

int ResManager::rmdir_recursive(string delpath) {
	T("ResManager:rmdir_recursive")
	DIR * pdir;
	pdir = opendir(delpath.c_str());
	if(!pdir) { E("ResManager:rmdir_recursive: " << "unable to open directory.") return 0; }
	struct dirent *pfile;
	struct stat buf;
	while((pfile = readdir(pdir))) {
		if(strcmp(pfile->d_name,".") == 0 || strcmp(pfile->d_name,"..") == 0) continue;
			if(stat( 
				(delpath +  
				#if defined(WIN32) || defined(_WIN32)
				string("\\") 
				#else
				string("/")
				#endif
				+ string(pfile->d_name)).c_str() , &buf)) { E("ResManager: Error getting attribute")}
			if(S_ISDIR(buf.st_mode)) {
				return rmdir_recursive(
				delpath + 
				#if defined(WIN32) || defined(_WIN32)
				string("\\")
				#else
				string("/")
				#endif
				+ string(pfile->d_name)
				);
			}// recursively call subdir
			else {
				if(remove((delpath + 
				#if defined(WIN32) || defined(_WIN32)
				string("\\") 
				#else
				string("/") 
				#endif
				+ string(pfile->d_name) ).c_str() )) { E("ResManager:rmdir_recursive: failed to remove " << pfile->d_name)}
				else { M("ResManager:rmdir_recursive: removed " << pfile->d_name)}
			}
	}
	M("ResManager:rmdir_recursive: Removing: "<< delpath)
	if(remove(delpath.c_str())) { E("ResManager:rmdir_recursive: failed to remove " << delpath)}
}

string ResManager::getAbsolutePath(string relpath) {
	if(isInit == 0) return string("");
    return resroot + relpath;
}



void ResManager::LoadBundle(string packagepath) {
	M("ResManager:LoadBundle "  <<  "7z x -y -o" + resroot + " " + packagepath )
	if(isInit == 0) { M("ResManager: isInit = 0. Quiting" ) return; }
	int hnd;
		
	system(((string("7z x -y -o") + resroot + string(" ") + packagepath).c_str()));
//#if defined(WIN32) || defined(_WIN32)
 //   	hnd = win32_run_process(archivemanagerpath + " x -y -o" + resroot + " " + packagepath  );
//#else
//#endif
  //  if(hnd) {
   //     M("ResManager:LoadBundle Waiting for 7z to finish")
//	#if defined(WIN32) || defined(_WIN32) 
 //       win32_process_wait(hnd,0);
//#else
//#endif
        M("ResManager:LoadBundle Done")
  //  }

    int loc = packagepath.find_last_of(
    #if defined(WIN32) || defined (_WIN32)
    "\\"
    #else
    "/"
    #endif
    );

    packagepath = resroot + 
    #if defined(WIN32) || defined(_WIN32)
    "res.7z";
	#else
	"/res.7z";
	#endif
    M( packagepath )
    system(((string("7z x -y -o") + resroot + 
    #if defined(WIN32) || defined(_WIN32)
    string("\\")
    #else
    string("/")
    #endif 
    + string("res ") + packagepath).c_str()));
//#if defined(WIN32) || defined(_WIN32)
 //   hnd = win32_run_process(archivemanagerpath + " x -y -o" + resroot + "res " + packagepath );
 //   if(hnd) {
  //      M("ResManager:LoadBundle Waiting for 7z to finish")
   //     win32_process_wait(hnd,0);
        M("ResManager:LoadBundle Done")
 //   }
//#else

//#endif
#ifdef DEBUG
system(((string("find ") + resroot).c_str()));
#endif
}



#if defined(WIN32) || defined(_WIN32)
int ResManager::DeleteAllFiles(string delpath)
{
    M("ResManager:DeleteAllFiles " << delpath)
	// Declare variables
	WIN32_FIND_DATA find_data;	HANDLE hFile;	int fileattrib;

    string search_path = delpath + string("*.*");
    string curfile;
    
	
	hFile = FindFirstFile(search_path.c_str() , &find_data);

	if (hFile != INVALID_HANDLE_VALUE) {
        do {
            curfile = find_data.cFileName;
            
            M("RemoveFile: " << curfile)
            if(curfile == "." || curfile == "..") { M("skipped") continue; }
            fileattrib = GetFileAttributes( (delpath + curfile).c_str() );
        
            if (fileattrib & FILE_ATTRIBUTE_READONLY) {
                M("Readonly: " << curfile << " modifying ")
				fileattrib &= ~FILE_ATTRIBUTE_READONLY;
				SetFileAttributes((delpath + curfile).c_str(), fileattrib);
			}

			// See if the file is a directory
			if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 				DeleteAllFiles(delpath + curfile + string("\\"));
			else { 
                M("DeleteFile" << delpath + curfile ) 
                int res =  DeleteFile((delpath + curfile).c_str() ); 
                if(res==0) E("ErrorDeletingFile: " << GetLastError())
                
            }

        }while(FindNextFile(hFile, &find_data));
    }	
    FindClose(hFile);

	M("DeleteDir " << delpath )
	RemoveDirectory(delpath.c_str());

    return 1;
}


int ResManager::win32_run_process(string cmdline)
{
    PROCESS_INFORMATION pi;
    STARTUPINFO si;

    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);


    
    if (CreateProcess(NULL, (char*)cmdline.c_str(), NULL, NULL, TRUE, 0, NULL,	NULL, &si, &pi))
    {
	    CloseHandle(pi.hThread);
	    return (int)pi.hProcess;
    }
    else{
	    return 0;
    }
}

int ResManager::win32_process_wait(int h, int t)
{
    if (WaitForSingleObject((HANDLE)h, (t ? 0 : INFINITE)) == WAIT_OBJECT_0)
    {
        DWORD ret;
        if (GetExitCodeProcess((HANDLE)h, &ret))
        {
            CloseHandle((HANDLE)h);
            return 1;
        }
    }
    
    return 0;
}



/*credit : http://www.codeguru.com/forum/archive/index.php/t-301326.html */

DWORD ResManager::CreateDirWithSecurity(LPCTSTR lpPath)
{
/*


SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE);
SetSecurityDescriptorGroup(&sd,NULL, FALSE );
SetSecurityDescriptorSacl(&sd, FALSE, NULL, FALSE );

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;
return 0;
*/
	SECURITY_ATTRIBUTES sa;
	SECURITY_DESCRIPTOR sd;
	PACL pAcl = NULL;
	DWORD cbAcl = 0,dwNeeded = 0,dwError = 0;
	HANDLE hToken;
	PTOKEN_USER ptu = NULL;

	if(!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken))
		return GetLastError();


	GetTokenInformation( hToken, TokenUser, NULL, 0, &dwNeeded);
	if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
		dwError = GetLastError();
		goto cleanup;
	}

	ptu = (TOKEN_USER*)malloc(dwNeeded);
	if (!GetTokenInformation(hToken, TokenUser, ptu, dwNeeded, &dwNeeded)) {
		dwError = GetLastError();
		goto cleanup;
	}

	cbAcl = sizeof(ACL) + ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(ptu->User.Sid));
	pAcl = (ACL*) malloc (cbAcl);

	if(!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) {
		dwError = GetLastError();
		goto cleanup;
	}

	if(!AddAccessAllowedAce(pAcl,ACL_REVISION,GENERIC_ALL|STANDARD_RIGHTS_ALL|SPECIFIC_RIGHTS_ALL,ptu->User.Sid)){
		dwError = GetLastError();
		goto cleanup;
	}

	InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);

	SetSecurityDescriptorDacl(&sd,TRUE,pAcl,FALSE);
	SetSecurityDescriptorOwner(&sd,ptu->User.Sid,FALSE);
	SetSecurityDescriptorGroup(&sd,NULL,FALSE);
	SetSecurityDescriptorSacl(&sd, FALSE,NULL,FALSE);

	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = &sd;
	sa.bInheritHandle = TRUE;

	CreateDirectory(lpPath, &sa);

	dwError = GetLastError();

cleanup:
	if(ptu) free(ptu);
	if(pAcl) free(pAcl);

	CloseHandle(hToken);
	return dwError;

}

#endif

